package com.coamc.xlsunit;

import java.io.IOException;
import java.io.Reader;
import java.util.Map;
import java.util.Stack;

import org.beetl.core.Configuration;
import org.beetl.core.Context;
import org.beetl.core.Event;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Listener;
import org.beetl.core.Resource;
import org.beetl.core.engine.DefaultTemplateEngine;
import org.beetl.core.engine.StatementParser;
import org.beetl.core.exception.BeetlException;
import org.beetl.core.exception.ScriptEvalError;
import org.beetl.core.resource.MapResourceLoader;
import org.beetl.core.statement.Expression;
import org.beetl.core.statement.GrammarToken;
import org.beetl.core.statement.Program;
import org.beetl.core.statement.Statement;
import org.beetl.core.statement.VarAttribute;
import org.beetl.core.statement.VarRef;

/**
 * excel 里的表达式计算引擎
 * @author xiandafu
 *
 */
public class ExpressionVal {
	static ExpressionVal val = new ExpressionVal();
	GroupTemplate gt = null;
	MapResourceLoader resourceLoader = null;
	private ExpressionVal(){
		resourceLoader = new MapResourceLoader();
		Configuration cfg = null;
		try {
			cfg = Configuration.defaultConfiguration();
		} catch (IOException e) {
			//不可能发生
			e.printStackTrace();
		}
		cfg.setEngine("com.coamc.xlsunit.ExpressionVal$OnlineTemplateEngine");
		gt = new GroupTemplate(resourceLoader, cfg);
	}
	public static ExpressionVal instance(){
		return val;
	}
	
	/** 计算excel里输入表达式 ，输入{a+b},需要去掉首尾的花括号才能处理
	 * @param table
	 * @param express
	 * @return
	 */
	public Object calc(VariableTable table,String express){
	
		if(!resourceLoader.containsKey(express)){
			//用脚本本生做key，因为脚本不可能太长
			express = express.substring(1,express.length()-1);
			String script = "var _$xiandafu= ("+express+");";
			resourceLoader.put(express, script);
		}
		
		Map map;
		try {
			map = gt.runScript(express, table.vars);
		} catch (ScriptEvalError e) {
			throw new RuntimeException("计算表达式错误:"+express,e);
		}
		//test
		return map.get("_$xiandafu");
	}
	
	public static void main(String[] args){
		VariableTable table = new VariableTable();
		table.add("a.b",1);
		ExpressionVal val = ExpressionVal.instance();
		Object ret = val.calc(table, "{a.b+1}");
		System.out.println(ret);
		
	}
	
	
	public static  class OnlineTemplateEngine extends DefaultTemplateEngine
	{
	        public Program createProgram(Resource resource, Reader reader, Map<Integer, String> textMap, String cr,
	                        GroupTemplate gt)
	        {
	                Program program = super.createProgram(resource, reader, textMap, cr, gt);
	                modifyStatemetn(resource,program,gt);
	                return program;
	        }
	        private void modifyStatemetn(Resource resource,Program program,GroupTemplate gt){
	                Statement[] sts = program.metaData.statements;
	                StatementParser parser = new StatementParser(sts, gt, resource.getId());
	                parser.addListener(VarRef.class, new VarRefNodeListener());
//	                parser.addListener(GeneralForStatement.class, new RestrictLoopNodeListener());
	                parser.parse();
	        }
	}
	
	static class VarRefNodeListener implements Listener
	{

	        @Override
	        public Object onEvent(Event e)
	        {
	                Stack stack = (Stack) e.getEventTaget();
	                Object o = stack.peek();
	                if (o instanceof VarRef)
	                {
	                	VarRef gf = (VarRef) o;
	                	XLSUnitVarRef rf = new XLSUnitVarRef(gf);
	                        return rf;
	                }

	                else
	                {
	                        return null;
	                }
	        }
	}
	static class XLSUnitVarRef extends VarRef{
		public XLSUnitVarRef(VarRef ref){
			super(ref.attributes,ref.hasSafe,ref.safe,ref.token);
		}
		
//		public XLSUnitVarRef(VarAttribute[] attributes, boolean hasSafe, Expression safe, GrammarToken token,
//				GrammarToken firstToken){
//			super(attributes, hasSafe, safe, token),firstToken);
//		}
//		

		@Override
		public Object evaluate(Context ctx)
		{

			Object value = ctx.vars[varIndex];
			if (value == Context.NOT_EXIST_OBJECT)
			{
				if (hasSafe)
				{
					return safe == null ? null : safe.evaluate(ctx);
				}
				else
				{
					/**
					 * 如果第一个变量不存在，考虑获取整个变量表达式代表的变量
					 * 比如a.b, 如果a不存在，则从变量表里获取"a.b"变量
					 */
					if(ctx.globalVar.containsKey(this.token.text)){
						value = ctx.getGlobal(this.token.text);
						return value;
					}else{
						BeetlException ex = new BeetlException(BeetlException.VAR_NOT_DEFINED);
						ex.pushToken(this.token);
						throw ex;
					}
					
					
				}
			}
			
			return super.evaluate(ctx);

			
		}
		
	}
	
	
	
	
	
}
